package com.pactera.asmp.server.job.dispatch.impl.compete;

import com.pactera.asmp.server.common.constants.DeviceStatus;
import com.pactera.asmp.server.common.constants.OccupyStatus;
import com.pactera.asmp.server.dao.*;
import com.pactera.asmp.server.entity.TaskDTO;
import com.pactera.asmp.server.entity.TaskDevice;
import com.pactera.asmp.server.entity.TaskDeviceExample;
import com.pactera.asmp.server.entity.TestReportSearchDTO;
import com.pactera.asmp.server.pojo.TestReport;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.util.StringUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class CompeteDataRepository {


    @Autowired
    private TaskDeviceMapper taskDeviceMapper;

    @Autowired
    private TaskMapper taskMapper;

    @Autowired
    private SubtaskMapper subtaskMapper;

    @Autowired
    private TestReportMapper testReportMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private DeviceInfoMapper deviceInfoMapper;

    private static final long REDIS_KEY_DUID_EXPIRE_SECOND= 1;

    public static final String REDIS_KEY_JOB_OCCUPY_DEVICE_INSTANCE = "JOB_OCCUPY_DEVICE_INSTANCE";


    public List<TestReport> getTestReport(Integer taskId){
        TestReportSearchDTO testReportSearchDTO = new TestReportSearchDTO();
        testReportSearchDTO.setTaskId(taskId);
        List<TestReport> list = testReportMapper.select2(testReportSearchDTO);
        return list;
    }

    @Transactional
    public int modifyTestReportCount(TaskDTO taskDTO){
        List<TestReport> list = getTestReport(taskDTO.getId());
        int offset = taskDTO.getScriptIds().split(",").length;
        int count = 1;
        int excSuccessNum = 0;
        for(int i = 0; i<list.size(); i=i+offset){
            boolean success = true;
            for(int j=0;j<offset;j++){
                if(i+j>=list.size()){
                    break;
                }
                TestReport tr = list.get(i+j);
                tr.setCount(count);
                testReportMapper.update(tr);
                if(tr.getStatus() != 1){
                    success = false;
                }
            }
            count++;
            if(success){
                excSuccessNum ++;
            }
        }
        taskDTO.setSuccessNumber(excSuccessNum);
        taskDTO.setFailNumber(taskDTO.getExeNumber() - excSuccessNum);
        taskMapper.updateHistory(taskDTO);

        TaskDTO task = taskMapper.getTaskById(taskDTO.getParentId());
        if(null != task) {
            if(task.getType() != 4){
                task.setSuccessNumber(taskDTO.getSuccessNumber());
                task.setFailNumber(taskDTO.getFailNumber());
            }
            taskMapper.update(task);
        }
        return count;
    }

    /**
     * 同一时间多线程之间互斥即可
     * @param duid
     * @return
     */
    private boolean tryLock(String duid) {
        String key = new StringBuilder(REDIS_KEY_JOB_OCCUPY_DEVICE_INSTANCE).append(":").append(duid).toString();
        return stringRedisTemplate.opsForValue().setIfAbsent(key, duid, REDIS_KEY_DUID_EXPIRE_SECOND, TimeUnit.SECONDS);
    }

    /**
     * 解锁
     * @param duid
     * @return
     */
    private boolean unLock(String duid) {
        String key = new StringBuilder(REDIS_KEY_JOB_OCCUPY_DEVICE_INSTANCE).append(":").append(duid).toString();
        return stringRedisTemplate.delete(key);
    }

    public TaskDTO getTaskDTO(Integer taskId){
        return taskMapper.getTaskHistoryById(taskId);
    }

    @Transactional
    public boolean modifyOccupyStatus(TaskDevice taskDevice ,int occupyStatus){
        String duid = taskDevice.getDuid();
        boolean isLock = tryLock(duid);
        if(!isLock){
            return false;
        }
        try{
            taskDevice.setOccupyStatus(occupyStatus);
            taskDeviceMapper.updateByPrimaryKey(taskDevice);
            // 获取占用者信息
            TaskDTO taskDTO = taskMapper.getTaskHistoryById2(taskDevice.getTaskId().intValue());
            if(occupyStatus != OccupyStatus.RELEASED.getValue()) {
                deviceInfoMapper.updateAutoTestStatus1(taskDevice.getDuid(), taskDTO.getUserId(), taskDTO.getUserName());
                if (!StringUtils.isEmpty(taskDevice.getAuxDuid())) {
                    deviceInfoMapper.updateAutoTestStatus1(taskDevice.getAuxDuid(), taskDTO.getUserId(), taskDTO.getUserName());
                }
            }
        }catch (Exception e){
            log.error("modifyOccupyStatus error duid="+duid,e);
            return false;
        }finally {
            unLock(duid);
        }
        return true;
    }

    public TaskDevice getTaskDevice(Long taskDeviceId){
        return taskDeviceMapper.selectByPrimaryKey(taskDeviceId);
    }

    public List<TaskDevice> getTaskDeviceListById(Integer taskId){
        TaskDeviceExample taskDeviceExample = new TaskDeviceExample();
        taskDeviceExample.createCriteria().andTaskIdEqualTo(Long.valueOf(taskId)).andIsDeletedEqualTo(0);
        return taskDeviceMapper.selectByExample(taskDeviceExample);
    }

    public List<TaskDevice> getTaskDeviceListByDeviceAndStatus(String duid, int occupyStatus) {
        TaskDeviceExample taskDeviceExample = new TaskDeviceExample();
        taskDeviceExample.createCriteria().andDuidEqualTo(duid).andOccupyStatusEqualTo(occupyStatus).andIsDeletedEqualTo(0);
        return taskDeviceMapper.selectByExample(taskDeviceExample);
    }

    @Transactional
    public boolean modifyTaskDeviceStatus(TaskDevice taskDevice, int occupyStatus){
        taskDevice.setOccupyStatus(occupyStatus);
        int rslt = taskDeviceMapper.updateByPrimaryKey(taskDevice);
        return rslt>0;
    }

    @Transactional
    public void releaseDevice(String duid){
        deviceInfoMapper.updateStatusByDuid(duid, DeviceStatus.FREE.getValue(), LocalDateTime.now());
    }
}
